package deepr.db;

import com.alibaba.fastjson.JSONObject;
import deepr.anno.ActionType;
import deepr.exception.DeeprException;
import deepr.lib.BeanMapConvert;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.*;
import org.neo4j.driver.internal.InternalRecord;
import org.neo4j.driver.summary.SummaryCounters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Repository
@Data
public class Neo4jDb implements GraphMapper {

  @Resource
  private Driver driver;

  @Autowired
  private DeeprException tipOpts;

  public Session session() {
    return driver.session();
  }

  public Record insert(String str, Object obj, Map<String, Object> params) throws DeeprException, IllegalAccessException {
    Map<String, Object> v = BeanMapConvert.beanToMap(obj);
    v.putAll(params);
    return insert(str, v);
  }

  public Record update(String str, Object obj, Map<String, Object> params) throws DeeprException, IllegalAccessException {
    Map<String, Object> v = BeanMapConvert.beanToMap(obj);
    v.putAll(params);
    return update(str, v);
  }

  public Record delete(String str, Object obj, Map<String, Object> params) throws DeeprException, IllegalAccessException {
    Map<String, Object> v = BeanMapConvert.beanToMap(obj);
    v.putAll(params);
    return delete(str, v);
  }

  public Record createRelation(String str, Object obj, Map<String, Object> params) throws DeeprException, IllegalAccessException {
    Map<String, Object> v = BeanMapConvert.beanToMap(obj);
    v.putAll(params);
    return createRelation(str, v);
  }

  public Record deleteRelation(String str, Object obj, Map<String, Object> params) throws DeeprException, IllegalAccessException {
    Map<String, Object> v = BeanMapConvert.beanToMap(obj);
    v.putAll(params);
    return deleteRelation(str, v);
  }

  public Record insert(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.INSERT);
    return get_one(str, value);
  }

  public Record update(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.UPDATE);
    return get_one(str, value);
  }

  public Record delete(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.DELETE);
    return get_one(str, value);
  }


  public Record infoAndCheck(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.INFO);
    return get_one(str, value);
  }

  public Record createRelation(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.CREATE_RELATION);
    return get_one(str, value);
  }

  public Record deleteRelation(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.DELETE_RELATION);
    return get_one(str, value);
  }

  public List<Record> list(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.LIST);
    return get_all(str, value);
  }


  public Record info(String str, Map<String, Object> value) throws DeeprException {
    tipOpts.setActionType(ActionType.INFO);
    tipOpts.setTip(false);
    return get_one(str, value);
  }


  public List<Record> get_all(String str, Map<String, Object> value) throws DeeprException {
    Session session = session();
    if (tipOpts.getActionType() == null)
      tipOpts.setActionType(ActionType.LIST);
    Result result = session.run(str, value);
    return checkSuccess(result, str, value);
  }

  public Record get_one(String str, Map<String, Object> value) throws DeeprException {
    if (tipOpts.getActionType() == null)
      tipOpts.setActionType(ActionType.INFO);
    List<Record> list = get_all(str, value);
    log.debug("list: {}", list);
    if (list.size() == 0)
      return new InternalRecord(new ArrayList<>(), new Value[]{});
    return list.get(0);
  }

  private List<Record> checkSuccess(Result result, String str, Map<String, Object> value) throws DeeprException {
    List<Record> rows = result.list();
    int v = rows.size();
    if (tipOpts.isTip()) {
      SummaryCounters sc = result.consume().counters();
      switch (tipOpts.getActionType()) {
        case INSERT:
          v = sc.nodesCreated();
          break;
        case UPDATE:
          v = sc.propertiesSet();
          break;
        case DELETE:
          v = sc.nodesDeleted();
          break;
        case CREATE_RELATION:
          v = sc.relationshipsCreated();
          break;
        case DELETE_RELATION:
          v = sc.relationshipsDeleted();
          break;
        default:
          break;
      }
      if (v == 0) {
        log.error("Run Neo4j Query Error, \n{}, params: {}", str, JSONObject.toJSONString(value));
        throw new DeeprException(tipOpts.getMessage());
      }
    }
    return rows;
  }


  public List<Record> queryList(String sql, Map<String, Object> filter) throws DeeprException {
    getTipOpts().setTip(false);
    return list(sql, filter);
  }

  public Record queryInfo(String sql, Map<String, Object> filter) throws DeeprException {
    return info(sql, filter);
  }

}
